/*
 * Copyright 2007 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 * ---
 *  Axel: modified ArrayList to have a special int vector..
 */
package org.axed.user.client.util;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import java.util.ArrayList;
import org.axed.user.client.AxedInternalError;

/**
 * Once GWT is java 1.5 compatbile this class should be able to simply be replaced
 * with a Vector&lt;int&gt;
 */
public class IntList {

  private static native void addImpl(JavaScriptObject array, int index, int o) /*-{
    array.splice(index, 0, o);
  }-*/;

  private static native int getImpl(JavaScriptObject array, int index) /*-{
    return array[index];
  }-*/;

  private static native void removeRangeImpl(JavaScriptObject array, int index,
      int count) /*-{
    array.splice(index, count);
  }-*/;

  private static native void setImpl(JavaScriptObject array, int index, int o) /*-{
    array[index] = o;
  }-*/;

  private static native void setSizeImpl(JavaScriptObject array, int newSize) /*-{
    array.length = newSize;
  }-*/;

  /**
   * This field holds a JavaScript array.
   */
  private transient JavaScriptObject array;

  /**
   * The size of the array.
   */
  private int size;

  /**
   * Used in hosted mode.
   */
  private ArrayList jarray;

  {
	if (GWT.isScript()) {
      clearImpl();
    }
  }

  public IntList() {
    if (!GWT.isScript()) {
       jarray = new ArrayList();
	}
  }


  public void add(int index, int o) {
	if (GWT.isScript()) {
      if (index < 0 || index > size) {
        indexOutOfBounds(index);
      }
      addImpl(array, index, o);
      ++size;
	} else {
      jarray.add(index, new Integer(o));
	}
  }

  public boolean add(int o) {
    if (GWT.isScript()) {
      setImpl(array, size++, o);
	} else {
      jarray.add(new Integer(o));
	}
    return true;
  }

  public void clear() {
    if (GWT.isScript()) {
      clearImpl();
	} else {
      jarray.clear();
	}
  }

  public boolean contains(int o) {
    if (GWT.isScript()) {
      return (indexOf(o) != -1);
	} else {
      return jarray.contains(new Integer(o));
	}
  }

  public int get(int index) {
    if (GWT.isScript()) {
      if (index < 0 || index >= size) {
        indexOutOfBounds(index);
      }
      return getImpl(array, index);
	} else {
	  return ((Integer) jarray.get(index)).intValue();
	}
  }

  public int indexOf(int o) {
    if (GWT.isScript()) {
      return indexOf(o, 0);
	} else {
      return jarray.indexOf(new Integer(o));
	}
  }

  public boolean isEmpty() {
    if (GWT.isScript()) {
      return size == 0;
	} else {
      return jarray.isEmpty();
	}
  }

  public int lastIndexOf(int o) {
    if (GWT.isScript()) {
      return lastIndexOf(o, size() - 1);
	} else {
      return jarray.lastIndexOf(new Integer(o));
	}
  }

  public int remove(int index) {
    if (GWT.isScript()) {
      int previous = get(index);
      removeRangeImpl(array, index, 1);
      --size;
      return previous;
	} else {
      return ((Integer) jarray.remove(index)).intValue();
	}
  }

  public boolean removeElement(int o) {
    if (GWT.isScript()) {
      int i = indexOf(o);
      if (i == -1) {
        return false;
      }
      remove(i);
      return true;
	} else {
	  return jarray.remove(new Integer(o));
	}
  }

  public int set(int index, int o) {
    if (GWT.isScript()) {
      int previous = get(index);
      setImpl(array, index, o);
      return previous;
	} else {
      return ((Integer) jarray.set(index, new Integer(o))).intValue();
	}
  }

  public int size() {
    if (GWT.isScript()) {
      return size;
	} else {
      return jarray.size();
	}
  }

  protected int indexOf(int o, int index) {
    if (!GWT.isScript()) {
      GWT.log("Js implementation of IntList called in hosted mode, this should not happen", null);
	}
    if (index < 0) {
      indexOutOfBounds(index);
    }
    for (; index < size; ++index) {
      if (o == getImpl(array, index)) {
        return index;
      }
    }
    return -1;
  }

  protected int lastIndexOf(int o, int index) {
    if (!GWT.isScript()) {
      GWT.log("Js implementation of IntList called in hosted mode, this should not happen.", null);
	}
    if (index >= size) {
      indexOutOfBounds(index);
    }
    for (; index >= 0; --index) {
      if (o == getImpl(array, index)) {
        return index;
      }
    }
    return -1;
  }

  public void removeRange(int fromIndex, int endIndex) {
    if (GWT.isScript()) {
      if (fromIndex < 0 || fromIndex >= size) {
        indexOutOfBounds(fromIndex);
      }
      if (endIndex < fromIndex || endIndex > size) {
        indexOutOfBounds(endIndex);
      }
      int count = endIndex - fromIndex;
      removeRangeImpl(array, fromIndex, count);
      size -= count;
	} else {
      GWT.log("Cannot call IntList.removeRange() :-(", null);
	  return;
	}
  }

  /**
   * This function sets the size of the array, and is used by Vector.
   */
  protected void setSize(int newSize) {
    if (!GWT.isScript()) {
      GWT.log("Js implementation of IntList called in hosted mode, this should not happen.", null);
	}
    if (newSize < 0) {
      indexOutOfBounds(newSize);
    }
    setSizeImpl(array, newSize);
    // null fill any new slots if size < newSize
    for (; size < newSize; ++size) {
      setImpl(array, size, 0);
    }
    // assignment necessary when size > newSize
    size = newSize;
  }

  private static native JavaScriptObject createArray() /*-{
    return [];
  }-*/;

  private void clearImpl() {
    if (!GWT.isScript()) {
      GWT.log("Js implementation of IntList called in hosted mode, this should not happen.", null);
	}
    array = createArray();
    size = 0;
  }

  private void indexOutOfBounds(int i) {
    GWT.log("intlist.indexOutOfBounts("+i+")", null);
	throw new AxedInternalError("intList.indexOutOfBounts("+i+")");
  }
}
